﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using BusinessEntities;
using BusinessLayer;
using Draco.DataStructure;

namespace WindowsFormsApp
{
    public partial class MetroGraphView : UserControl
    {
        public MetroGraphView()
        {
            InitializeComponent();

            //优化绘图
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer
                     | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
        }

        private MetroGraph graph = new MetroGraph();
        private int scrollX;
        private int scrollY;
        private float zoomScale = 1;
        private MetroNode startNode;
        private MetroNode endNode;
        private MetroPath curPath;

        private D_LinkList<ILocatingPoint> sites = new D_LinkList<ILocatingPoint>();


        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public MetroGraph Graph
        {
            get { return graph; }
        }

        /// <summary>
        /// 获取或设置当前水平滚动量。
        /// </summary>
        [Description("当前水平滚动量。"), DefaultValue(0)]
        public int ScrollX
        {
            get { return scrollX; }
            set
            {
                if (scrollX != value)
                {
                    scrollX = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置当前垂直滚动量。
        /// </summary>
        [Description("当前垂直滚动量。"), DefaultValue(0)]
        public int ScrollY
        {
            get { return scrollY; }
            set
            {
                if (scrollY != value)
                {
                    scrollY = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置当前缩放比例。
        /// </summary>
        [Description("当前缩放比例。"), DefaultValue(1)]
        public float ZoomScale
        {
            get { return zoomScale; }
            set
            {
                if (zoomScale != value)
                {
                    zoomScale = Math.Max(value, 0.1f);
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置起点。
        /// </summary>
        [Browsable(false)]
        public MetroNode StartNode
        {
            get { return startNode; }
            set { startNode = value; }
        }

        /// <summary>
        /// 获取或设置终点。
        /// </summary>
        [Browsable(false)]
        public MetroNode EndNode
        {
            get { return endNode; }
            set { endNode = value; }
        }

        /// <summary>
        /// 获取或设置当前乘车路线。该属性始终不为空引用。
        /// </summary>
        [Browsable(false)]
        public MetroPath CurPath
        {
            get
            {
                if (curPath == null)
                    curPath = new MetroPath();
                return curPath;
            }
            set { curPath = value; }
        }

        [Browsable(false)]
        public D_LinkList<ILocatingPoint> Sites
        {
            get { return sites; }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);


            //消除锯齿
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            //绘制地铁线路图
            PaintGraph(e.Graphics, this.Graph);

            //绘制当前乘车路线
            PaintCurPath(e.Graphics);

            //绘制起点和终点
            PaintStartAndEndNodes(e.Graphics);

            //绘制线路列表
            PaintLineList(e.Graphics);

            //绘制选定地点
            PaintSites(e.Graphics);

        }

        /// 绘制线路列表
        private void PaintLineList(Graphics g)
        {
            if (this.Graph.Lines.Count == 0) return;

            g.ResetTransform();

            //遮罩层
            Rectangle rc = new Rectangle(10, 10, 150, (this.Graph.Lines.Count + 1) * 15);
            using (Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)))
            {
                g.FillRectangle(brush, rc);
            }
            g.DrawRectangle(Pens.Gray, rc);

            //线路列表
            int y = rc.Y + 15;
            foreach (MetroLine line in this.Graph.Lines)
            {
                using (Pen pen = new Pen(Color.FromArgb(line.Color), 5))
                {
                    g.DrawLine(pen, rc.X + 15, y, rc.X + 70, y);
                }

                var sz = g.MeasureString(line.Name, this.Font);
                g.DrawString(line.Name, this.Font, Brushes.Black, rc.X + 80, y - sz.Height / 2);

                y += 15;
            }
        }


        // 绘制地铁线路图
        private void PaintGraph(Graphics g, MetroGraph graph)
        {
            //绘制地铁路径
            foreach (MetroLink link in graph.Links)
            {
                PaintLine(g, link.From.X, link.From.Y, link.To.X, link.To.Y, Color.FromArgb(graph.LineDict[link.LineID].Color));
            }

            //绘制地铁站点
            foreach (MetroNode node in graph.Nodes)
            {
                PaintNode(g, node);
            }
        }

        // 绘制地铁站点
        private void PaintNode(Graphics g, MetroNode node)
        {
            //绘制站点圆圈
            Color color = node.Lines.Count > 1 ? Color.Black : Color.FromArgb(graph.LineDict[node.Lines[0]].Color);
            var rect = GetNodeRect(node);
            g.FillEllipse(Brushes.White, rect);
            using (Pen pen = new Pen(color))
            {
                g.DrawEllipse(pen, rect);
            }

            //绘制站点名称
            var sz = g.MeasureString(node.Name, this.Font).ToSize();
            Point pt = new Point(node.X - sz.Width / 2, node.Y + (rect.Height >> 1) + 4);
            g.DrawString(node.Name, Font, Brushes.Black, pt);
        }

        // 绘制线路
        private void PaintLine(Graphics g, int x1, int y1, int x2, int y2, Color color)
        {
            Point pt1 = new Point(x1, y1);
            Point pt2 = new Point(x2, y2);

            using (Pen pen = new Pen(color, 5))
            {
                pen.LineJoin = LineJoin.Round;
                g.DrawLine(pen, pt1, pt2);
            }
        }

        // 绘制起点和终点。
        private void PaintStartAndEndNodes(Graphics g)
        {
            //绘制起点
            if (this.StartNode != null)
            {
                var startNodeImage = Properties.Resources.StartNode;
                int sx = this.StartNode.X - startNodeImage.Width / 2;
                int sy = this.StartNode.Y - startNodeImage.Height;
                g.DrawImage(startNodeImage, sx, sy);
            }

            //绘制终点
            if (this.EndNode != null)
            {
                var endNodeImage = Properties.Resources.EndNode;
                int ex = this.EndNode.X - endNodeImage.Width / 2;
                int ey = this.EndNode.Y - endNodeImage.Height;
                g.DrawImage(endNodeImage, ex, ey);
            }
        }

        // 绘制当前乘车路线。
        private void PaintCurPath(Graphics g)
        {
            if (this.CurPath.Links.Count == 0) return;

            //绘制白色遮罩层
            RectangleF rcMask = ClientToMetro(this.ClientRectangle);
            using (Brush brush = new SolidBrush(Color.FromArgb(200, Color.White)))
            {
                g.FillRectangle(brush, rcMask);
            }

            //绘制当前乘车路线
            foreach (MetroLink link in this.CurPath.Links)
            {
                //绘制路径
                PaintLine(g, link.From.X, link.From.Y, link.To.X, link.To.Y, Color.FromArgb(graph.LineDict[link.LineID].Color));

                //绘制站点
                PaintNode(g, link.From);
                PaintNode(g, link.To);
            }
        }

        // 将指定的坐标转换为地铁线路图坐标。
        private Point ClientToMetro(Point pt)
        {
            int x = (int)((pt.X - this.ScrollX) / this.ZoomScale);
            int y = (int)((pt.Y - this.ScrollY) / this.ZoomScale);
            return new Point(x, y);
        }

        // 将指定的矩形转换为地铁线路图坐标。
        private Rectangle ClientToMetro(Rectangle rect)
        {
            Point pt = ClientToMetro(rect.Location);
            return new Rectangle(pt.X, pt.Y, (int)(rect.Width / this.ZoomScale), (int)(rect.Height / this.ZoomScale));
        }

        // 获取地铁站点的矩形区域。
        private Rectangle GetNodeRect(MetroNode node)
        {
            int r = node.Links.Count > 2 ? 7 : 5;
            return new Rectangle(node.X - r, node.Y - r, (r << 1) + 1, (r << 1) + 1);
        }

        //绘制选定的地点
        private void PaintSites(Graphics g)
        {
            foreach (ILocatingPoint site in Sites)
            {
                var siteNodeImage = Properties.Resources.SiteNode;
                int ex = site.X - siteNodeImage.Width / 2;
                int ey = site.Y - siteNodeImage.Height;
                g.DrawImage(siteNodeImage, ex, ey);


                var rect = new Rectangle(site.X - 5, site.Y - 5, (5 << 1) + 1, (5 << 1) + 1);
                var sz = g.MeasureString(site.Name, this.Font).ToSize();
                Point pt = new Point(site.X - sz.Width / 2, site.Y + (rect.Height >> 1)-4);
                g.DrawString(site.Name, Font, Brushes.Black, pt);

            }
        }


        public void SetStartAndEndNodes(MetroNode start,MetroNode end)
        {
            if (start != null && end != null)
            {
                this.StartNode = start;
                this.EndNode = end;
            }
            else
            {
                StartNode = null;
                EndNode = null;
            }
            Invalidate();
        }

        public void SetCurPath(MetroPath path)
        {
            if (path != null && path.Links.Count>0)
            {
                this.CurPath = path;
                this.StartNode = path.GetStartNode();
                this.EndNode = path.GetEndNode();
            }
            else
            {
                StartNode = null;
                EndNode = null;
                this.CurPath.Links.Clear();
            }
            Invalidate();
        }

        public void SetSites(D_LinkList<ILocatingPoint> sites)
        {
            if (sites == null)
            {
                this.sites.Clear();
            }
            else
            {
                this.sites = sites;
            }
            Invalidate();
        }

    }
}
